/**
 * Copyright (c) 2011 Tomi Paananen.
 *
 * Part of Quick Gestures engine library.
 */

// Own header
#include "classifierthread.h"

// qgengine includes
#include "classifierif.h"
#include "featureextractor.h"
#include "gesture.h"
#include "simpleclassifier.h"
#include "qgtrace.h"

#ifdef QG_LOG
    #include "logger.h"
#endif


/*!
  \class ClassifierThread
  \brief Thread for running the classifier.
*/


/*!
  Constructor.
*/
ClassifierThread::ClassifierThread(QObject *parent /* = 0 */)
    : QThread(parent),
      m_classifier(0),
      m_featureExtractor(0),
      m_gesture(0),
      m_state(Idle),
      m_doCancel(false)
{
    // By default, use the simple classifier.
    m_classifier = new SimpleClassifier();
}


/*!
  Destructor.
*/
ClassifierThread::~ClassifierThread()
{
    delete m_classifier;
}


/*!
  Returns the state of the classifier thread.
*/
int ClassifierThread::state() const
{
    return m_state;
}


/*!
  Returns the classifier instance.
*/
ClassifierIf *ClassifierThread::classifier() const
{
    return m_classifier;
}


/*!
  Sets \a classifier as the classifier. This thread will take ownership of the
  given classifier.
*/
void ClassifierThread::setClassifier(ClassifierIf *classifier)
{
    m_classifier = classifier;
}


/*!
  Sets \a gestureSet as the reference gestures.
*/
void ClassifierThread::setGestureSet(QList<Gesture*> gestureSet)
{
    DEBUG_INFO(gestureSet.count() << "gestures.");
    m_gestureSet = gestureSet;
}


/*!
  Sets \a gesture as the gesture to recognize.
*/
void ClassifierThread::setGesture(Gesture *gesture)
{
    DEBUG_INFO((int)gesture);
    m_gesture = gesture;
}


/*!
  From QThread.
*/
void ClassifierThread::run()
{
    DEBUG_POINT;
    setState(Running);

    const int count(m_gestureSet.count());
    DEBUG_INFO("The gesture set contains" << count << "gestures.");

    if (count) {
        // Construct the feature extractor instance.
        m_featureExtractor = new FeatureExtractor();

        // Process the gesture.
        m_featureExtractor->processGesture(m_gesture);

        delete m_featureExtractor;
        m_featureExtractor = 0;
    }
    else {
        DEBUG_INFO("No gestures set!");
        emit failed(QGEngine::GestureSetNotSet);
        setState(Idle);
        return;
    }

    int bestError(0);
    int bestIndex(0);

    for (int i = 0; i < count; ++i) {
        if (m_doCancel) {
            DEBUG_INFO("Cancelling...");
            break;
        }

        int error = m_classifier->compare(m_gesture, m_gestureSet[i]);

        DEBUG_INFO("Error compared to gesture with ID"
                   << m_gestureSet[i]->id() << "was" << error);

        if (error < bestError || i == 0) {
            bestError = error;
            bestIndex = i;
        }

        emit progress(i * 100 / count);
    }

    if (m_doCancel) {
        emit cancelled();
        m_doCancel = false;
    }
    else {
        if (bestError < m_classifier->recognitionThreshold()) {
            DEBUG_INFO("The best result (" << bestError << ") is acceptable.");
            Gesture *gesture = m_gestureSet[bestIndex];
            emit succeeded(gesture->id(), gesture->name());
        }
        else {
            DEBUG_INFO("The threshold value ("
                       << m_classifier->recognitionThreshold() <<
                       ") was exceeded i.e. the best match (id:" <<
                       m_gestureSet[bestIndex]->id() << ") with value"
                       << bestError << "was not accurate enough.");
            emit failed(QGEngine::MaxRecognitionErrorExceeded);
        }
    }

    setState(Idle);
}


/*!
  Sets the state.
*/
void ClassifierThread::setState(ClassifierStates state)
{
    m_state = state;
    emit stateChanged(state);
}


/*!
  Cancels the recognition if ongoing.
*/
void ClassifierThread::cancel()
{
    if (m_state == Running) {
        m_doCancel = true;
    }
}


// End of file.
